﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Genuilder.Extensibility.Utilities;

namespace Genuilder.Extensibility
{
	public delegate void ContentChangedHandler(CodeItem sender);
	//public delegate void StateChangedHandler(CodeItem sender, );
	public delegate void StateChangedHandler(CodeItem sender, CodeItemState newState, CodeItemState oldState);

	public abstract class CodeItem : IExtensibleObject
	{
		readonly ExtensionRepository _Extensions;
		public CodeItem(string name, CodeItemState initialState)
		{
			if(name == null)
				throw new ArgumentNullException("name");
			State = initialState;
			_Name = name;
			_Extensions = new ExtensionRepository(this);
		}



		private readonly String _Name;
		public String Name
		{
			get
			{
				return _Name;
			}
		}

		internal TaskQueue TaskQueue
		{
			get;
			set;
		}

		internal ICodeRepository Repository
		{
			get;
			set;
		}
		internal DependencyNode DependencyNode
		{
			get;
			set;
		}

		public IEnumerable<CodeItem> Sources
		{
			get
			{
				return DependencyNode.Sources.Select(n => n.Item);
			}
		}

		public IEnumerable<CodeItem> Targets
		{
			get
			{
				return DependencyNode.Targets.Select(n => n.Item);
			}
		}

		public CodeDependency SourceOf(CodeItem item)
		{
			return DependencyNode.SourceOf(item);
		}

		public CodeDependency TargetOf(CodeItem item)
		{
			return DependencyNode.TargetOf(item);
		}


		public CodeDependency SourceOf(string item)
		{
			return SourceOf(Repository.Get(item));
		}

		public CodeDependency TargetOf(string item)
		{
			return TargetOf(Repository.Get(item));
		}



		public event ContentChangedHandler ContentChanged;
		public event StateChangedHandler StateChanged;
		public event StateChangedHandler Excluded;
		public event StateChangedHandler Deleted;




		internal bool _IsCreating;
		public String Content
		{
			get
			{
				//AssertIsNotCreating();
				return ContentCore;
			}
			set
			{
				//AssertIsNotCreating();
				ContentCore = value;
			}
		}

		private void AssertIsNotCreating()
		{
			if(_IsCreating)
				throw new InvalidOperationException("Cannot access content property when the CodeItem is creating, use the ContentChanged event instead.");
		}

		protected abstract String ContentCore
		{
			get;
			set;
		}

		//internal void ChangeContent(string value, bool fireEvent)
		//{
		//    Content = value;
		//    if(fireEvent)
		//        OnContentChanged();
		//}


		internal void OnContentChanged()
		{
			TaskQueue.Enqueue(this, "content", () =>
			{
				if(ContentChanged != null)
					ContentChanged(this);
			});
		}

		public void Exclude()
		{
			ChangeState(CodeItemState.Excluded);
		}

		public void Delete()
		{
			ChangeState(CodeItemState.Deleted);
			OnDeleteCore();
		}

		protected virtual void OnDeleteCore()
		{
		}


		bool CanChangeState(CodeItemState newState)
		{
			var currentState = State;
			if(newState == currentState)
				return true;
			var authorizedTransitions = new Dictionary<CodeItemState, CodeItemState[]>();
			authorizedTransitions.Add(CodeItemState.Exist, new[] { CodeItemState.Deleted, CodeItemState.Excluded });
			authorizedTransitions.Add(CodeItemState.Deleted, new CodeItemState[0]);
			//authorizedTransitions.Add(CodeItemState.Undefined, new[] { CodeItemState.Added, CodeItemState.Deleted, CodeItemState.Excluded, CodeItemState.Modified, CodeItemState.Nothing });
			authorizedTransitions.Add(CodeItemState.Excluded, new[] { CodeItemState.Deleted, CodeItemState.Exist });
			//authorizedTransitions.Add(CodeItemState.Nothing, new[] { CodeItemState.Modified, CodeItemState.Excluded, CodeItemState.Deleted });
			//authorizedTransitions.Add(CodeItemState.Modified, new[] { CodeItemState.Deleted, CodeItemState.Excluded });
			return authorizedTransitions[currentState].Contains(newState);
		}


		internal void ChangeState(CodeItemState newState)
		{
			var oldState = State;
			if(oldState == newState)
				return;
			if(!CanChangeState(newState))
				throw new InvalidOperationException(String.Format("cannot change state from {0} to {1}", oldState, newState));
			State = newState;
			OnStateChanged(newState, oldState);
		}

		private void OnStateChanged(CodeItemState newState, CodeItemState oldState)
		{
			TaskQueue.Enqueue(this, "state", () =>
			{
				if(StateChanged != null)
					StateChanged(this, newState, oldState);
				if(newState == CodeItemState.Deleted)
					if(Deleted != null)
						Deleted(this, newState, oldState);
				if(newState == CodeItemState.Excluded)
					if(Excluded != null)
						Excluded(this, newState, oldState);
			});
		}

		public CodeItemState State
		{
			get;
			private set;
		}

		#region IExtensibleObject Members

		public T GetExtension<T>()
		{
			return _Extensions.GetExtension<T>();
		}

		#endregion
	}

	public enum CodeItemState
	{
		Excluded,
		Deleted,
		Exist
	}
}
